package com.intco.ait.quotation.api.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Months;
import org.joda.time.format.DateTimeFormat;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @author Mxy
 */
@Slf4j
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {


    public static final String DATE_TIME_FORMAT_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd";

    public static Date GMT_ZERO;

    // 上海时区的区域标识符
    private static final ZoneId shZoneId = ZoneId.of("Asia/Shanghai");
    // 获取当前上海时区的偏移量
    private static final ZoneOffset shZoneOffset = shZoneId.getRules().getOffset(Instant.now());
    // 定义日期时间格式化器，使用上海时区
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_HH_mm_ss);

    static {
        try {
            GMT_ZERO = parseDate("1970-01-01 08:00:00", DATE_TIME_FORMAT_HH_mm_ss);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 将时间戳转换为字符串格式的日期时间。
     * @param timestamp 待转换的时间戳，单位为毫秒。
     * @return 格式化后的日期时间字符串，格式为"hh:mm:ss"。
     */
    public static String timestampToString(long timestamp) {
        // 将时间戳转换为指定时区的LocalDateTime
        LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), shZoneOffset);
        // 使用定义的格式化器格式化日期时间
        return FORMATTER.format(dateTime);
    }

    public static boolean isToday(LocalDateTime localDateTime) {
        return localDateTime != null && LocalDate.now().isEqual(localDateTime.toLocalDate());
    }

    public static boolean isToday(Date date) {
        return isToday(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
    }

    /**
     * 将日期转成整形的yyyyMM格式
     *
     * @param localDateTime 本地时间
     * @return 数值类型的年月
     */
    public static int getMonthYyyyMm(LocalDateTime localDateTime) {
        return localDateTime.getYear() * 100 + localDateTime.getMonthValue();
    }

    /**
     * 计算一个格式化的日期字符串，直接相差的月份
     *
     * @param startDate 起始时间
     * @param endDate   结束时间
     * @return 相差月份
     */
    public static int betweenMonths(String startDate, String endDate) {
        if (StringUtils.isBlank(startDate) || StringUtils.isBlank(endDate)) {
            throw new RuntimeException("时间参数错误");
        }
        DateTime start = (startDate.length() > 10 && !startDate.contains("T")) ? DateTime.parse(startDate, DateTimeFormat.forPattern(DATE_TIME_FORMAT_HH_mm_ss)) : DateTime.parse(startDate);
        DateTime end = (endDate.length() > 10 && !endDate.contains("T")) ? DateTime.parse(endDate, DateTimeFormat.forPattern(DATE_TIME_FORMAT_HH_mm_ss)) : DateTime.parse(endDate);
        return Months.monthsBetween(start, end).getMonths();
    }

    /**
     * 判断两个时间相差是否小于指定的天数
     *
     * @param startDate 起始时间
     * @param endDate   结束时间
     * @param days      允许相差的最大天数
     * @return 日期差是否在允许的范围内 true: 在许可范围内
     */
    public static boolean betweenInDays(LocalDateTime startDate, LocalDateTime endDate, long days) {
        return startDate != null && endDate != null && Duration.between(startDate, endDate).toDays() <= days;
    }

    /**
     * 判断第一个参数，是否在第二和第三之间（闭区间）
     *
     * @param date  待判定的日期参数
     * @param start 起始日期
     * @param end   结束日期
     * @return true:在日期范围内
     */
    public static boolean between(Date date, Date start, Date end) {
        if (date == null || start == null || end == null) {
            return false;
        }
        return date.getTime() >= start.getTime() && date.getTime() <= end.getTime();
    }

    /**
     * 获取 昨天的日期, 0时0分0秒
     */
    public static Date getYesterdayDate() {
        return new DateTime().minusDays(1).toDate();
    }


    /**
     * 获取指定日期前几天
     *
     * @param date   指定的日期
     * @param offset 偏移量
     * @return 偏移量前后的日期
     */
    public static Date getSomedayStart(Date date, int offset) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusDays(offset).toDate();
    }

    /**
     * 获取今天的日期，今天的0时0分0秒
     *
     * @author zhangqingqiang
     */
    public static Date getTodayDate() {
        return new DateTime().toDate();
    }

    /**
     * 获取指定日期当天的开始 0时0分0秒0毫秒
     *
     * @param date time
     * @author zhangqingqiang
     */
    public static Date toDayStart(Date date) {
        DateTime dateTime = new DateTime(date);
        return dateTime.withHourOfDay(0).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withMinuteOfHour(0).toDate();
    }

    /**
     * 获取指定日期当天的开始 23时59分59秒999毫秒
     *
     * @param date 日期参数
     * @return 指定日期当天的最大时间
     * @author zhangqingqiang
     */
    public static Date toDayEnd(Date date) {
        DateTime dateTime = new DateTime(toDayStart(date)).plusDays(1).minusMillis(1);
        return dateTime.toDate();
    }


    /**
     * 获取两个日期之间的天数
     *
     * @param startDate 起始日期
     * @param endDate   结束日期
     * @return 两个日期间相差的天数
     */
    public static int betweenDays(Date startDate, Date endDate) {
        DateTime start = new DateTime(startDate);
        DateTime end = new DateTime(endDate);
        return Days.daysBetween(start, end).getDays();
    }

    /**
     * 获取两个日期之间的天数
     *
     * @param startDate 起始日期
     * @param endDate   结束日期
     * @return date2 - date1 的天数
     * @author zhangqingqiang
     */
    public static int betweenDays(String startDate, String endDate) {

        DateTime start = new DateTime(startDate);
        DateTime end = new DateTime(endDate);
        return Days.daysBetween(start, end).getDays();
    }

    /**
     * @author tang aoqiang
     */
    public static List<Date> betweenDate(Date startDate, Date endDate) {
        List<Date> days = Lists.newArrayList();
        Calendar tempStart = Calendar.getInstance();
        tempStart.setTime(startDate);
        Calendar tempEnd = Calendar.getInstance();
        tempEnd.setTime(endDate);
        tempEnd.add(Calendar.DATE, +1);// 日期加1(包含结束)
        while (tempStart.before(tempEnd)) {
            days.add(tempStart.getTime());
            tempStart.add(Calendar.DAY_OF_YEAR, 1);
        }
        return days;

    }


    /**
     * 转化日期或者时间
     *
     * @param date date
     */
    public static Date parseDate(String date) {
        return DateTime.parse(date, DateTimeFormat.forPattern(DATE_TIME_FORMAT_HH_mm_ss)).toDate();
    }

    /**
     * 转化日期或者时间
     *
     * @param date date
     */
    public static Date parseDate(String date, String formatType) {
        return DateTime.parse(date, DateTimeFormat.forPattern(formatType)).toDate();
    }


    /**
     * 输入2017-03-03 11:22:33 会得到2017-03-03 00:00:00.000
     *
     * @param srcDate srcDate
     */
    public static Date getDateForZero(Date srcDate) {
        return toDayStart(srcDate);
    }

    /**
     * 输入2017-03-03 11:22:33 会得到2017-03-03 23:59:59.999
     *
     * @param source source
     */
    @Deprecated
    public static Date getDateFor59(Date source) {
        return toDayEnd(source);
    }


    /**
     * 判断当前时间是否在[startTime, endTime]区间，注意时间格式要一致
     *
     * @param nowTime   当前时间
     * @param startTime 开始时间
     * @param endTime   结束时间
     */
    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.equals(startTime) || nowTime.equals(endTime)) {
            return true;
        }
        return nowTime.after(startTime) && nowTime.before(endTime);
    }

    /**
     * 判断日期是否是周末
     *
     * @param date 日期
     * @return true 是周末 false 非周末
     */
    public static Boolean isWeekendDate(Date date) {
        Assert.notNull(date, "日期不能为空！");
        DateTime dateTime = new DateTime(date);
        return dateTime.getDayOfWeek() > 5;

    }

    public static String getTimeStr() {
        Calendar cal = Calendar.getInstance();
        return new SimpleDateFormat(DATE_TIME_FORMAT_HH_mm_ss).format(cal.getTime());
    }

    /**
     * Long类型转DateString
     *
     * @param currentTime currentTime
     * @param formatType  formatType
     */
    public static String longToString(long currentTime, String formatType)
            throws ParseException {
        // long类型转成Date类型
        Date date = longToDate(currentTime, formatType);
        // date类型转成String
        return dateToString(date, formatType);
    }

    /**
     * Long类型转Date
     *
     * @param currentTime currentTime
     * @param formatType  formatType
     */
    public static Date longToDate(long currentTime, String formatType) throws ParseException {
        // 根据long类型的毫秒数生命一个date类型的时间
        Date dateOld = new Date(currentTime);
        // 把date类型的时间转换为string
        String sDateTime = dateToString(dateOld, formatType);
        // 把String类型转换为Date类型
        return stringToDate(sDateTime, formatType);
    }

    /**
     * string转Date
     *
     * @param strTime    strTime
     * @param formatType formatType
     */
    public static Date stringToDate(String strTime, String formatType) throws ParseException {
        if (StringUtils.isEmpty(strTime)) {
            return null;
        }
        SimpleDateFormat formatter = new SimpleDateFormat(formatType);
        return formatter.parse(strTime);
    }

    /**
     * Date转string
     *
     * @param date       date
     * @param formatType formatType
     */
    public static String dateToString(Date date, String formatType) {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(formatType).format(date);
    }

    public static String dateToString(Date date) {
        return dateToString(date, DATE_TIME_FORMAT_HH_mm_ss);
    }

    public static String dateToDateString(Date date) {
        return dateToString(date, DATE_TIME_FORMAT);
    }
}